package cn.xnatural.enet.event;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * event context: 事件执行的上下文
 */
public class EC {
    /**
     * 一次事件执行的id. 用于追踪执行的是哪次事件
     */
    protected String id;
    /**
     * 是否追踪执行.用于调试
     */
    protected boolean track;
    /**
     * 强制异步. 如果设置了就会忽略 @{@link EL}中的设置
     */
    protected Boolean async;
    /**
     * 目标方法的参数
     */
    public Object[] args;
    /**
     * 是由哪个事件发布器发布的
     */
    protected EP ep;
    /**
     * 此次执行的事件名
     */
    public final String eName;
    /**
     * 事件执行完成的回调函数
     */
    protected Consumer<EC> completeFn;
    /**
     * 事件源
     */
    protected Object source;
    /**
     * 开始时间
     */
    protected long start;
    /**
     * 结果集
     */
    protected final List<ResultRecord> results = new LinkedList<>();
    /**
     * 要执行的事件链
     */
    protected List<Listener> willPass;
    /**
     * 成功执行过的事件监听
     */
    protected final Queue<Listener>     successPassed = new ConcurrentLinkedQueue();
    /**
     * 执行失败的事件监听
     */
    protected final Queue<Listener>     failPassed    = new ConcurrentLinkedQueue<>();
    /**
     * 是否结束
     */
    protected final AtomicBoolean       stopped       = new AtomicBoolean(false);
    /**
     * 属性集
     */
    protected final Map<Object, Object> attrs         = new ConcurrentHashMap<>(7);


    public EC(String eName) { this(eName, null); }

    /**
     *
     * @param eName 事件名
     * @param source 事件源
     */
    public EC(String eName, Object source) {
        if (eName == null || eName.isEmpty()) throw new IllegalArgumentException("Param eName required");
        this.eName = eName;
        this.source = source;
    }


    /**
     * 开始执行,初始化
     * @param ls 监听器
     * @param ep 事件中心
     */
    protected EC start(List<Listener> ls, EP ep) {
        start = System.currentTimeMillis();
        willPass = ls; this.ep = ep;
        if (track) { // 是否要追踪此条事件链的执行
            if (id() == null) id = UUID.randomUUID().toString().replaceAll("-", "");
            ep.log.info("ENET({},{}): source: {}", eName, id, source());
        }
        return this;
    }


    /**
     * 此次事件执行完成
     */
    protected void tryFinish() {
        if (stopped.get()) return;
        boolean noL = isNoListener();
        if (noL) {
            if (stopped.compareAndSet(false, true)) {
                if (track) ep().log.warn("ENET({},{}): no listener", eName, id());
                Consumer<EC> fn = completeFn();
                if (fn != null) fn.accept(this);
            }
        }
        else if (successPassed.size() + failPassed.size() == willPass.size()) {
            if (stopped.compareAndSet(false, true)) { // 防止并发时被执行多遍
                boolean f = isSuccess();
                if (f && track) ep().log.info("ENET({},{}): success end, result: {}, spend: {}ms", eName, id(), results(), System.currentTimeMillis() - start);
                else if (!f) ep().log.warn("ENET({},{}): fail end: {}, result: {}, spend: {}ms", eName, id(), ex().getMessage(), results(), System.currentTimeMillis() - start);
                Consumer<EC> fn = completeFn();
                if (fn != null) fn.accept(this);
            }
        }
    }



    /**
     * passed一个Listener 代表执行完成一个Listener.
     * @param l {@link Listener}
     * @param success 成功执行
     */
    protected EC passed(Listener l, boolean success) {
        if (success) successPassed.add(l);
        else failPassed.add(l);
        return this;
    }


    /**
     * 事件是否执行成功
     */
    public boolean isSuccess() {
        return ex() == null && !isNoListener() && willPass.size() == successPassed.size();
    }


    /**
     * 是否没有监听器
     */
    public boolean isNoListener() { return willPass == null || willPass.isEmpty(); }


    /**
     * 设置完成时回调函数
     * @param completeFn 完成时回调函数
     */
    public EC completeFn(Consumer<EC> completeFn) {
        if (ep != null) {
            ep().log.error("ENET({},{}): Cannot be changed during operation", eName, id());
            return this;
        }
        if (stopped.get()) {
            ep().log.error("ENET({},{}): Closed and cannot be changed", eName, id());
            return this;
        }
        this.completeFn = completeFn;
        return this;
    }


    /**
     * 获取事件结束函数
     */
    public Consumer<EC> completeFn() { return completeFn; }


    /**
     * 强制同步执行
     */
    public EC sync() { return async(false); }


    public EC async(Boolean async) {
        if (ep != null) {
            ep().log.error("ENET({},{}): Cannot be changed during operation", eName, id());
            return this;
        }
        if (stopped.get()) {
            ep().log.error("ENET({},{}): Closed and cannot be changed", eName, id());
            return this;
        }
        this.async = async; return this;
    }


    /**
     * 是否为强制异步执行
     */
    public Boolean isForceAsync() { return this.async; }


    /**
     * 设置debug模式, 日志打印 事件执行前后详情
     */
    public EC debug() {
        if (ep != null) {
            ep().log.error("ENET({},{}): Cannot be changed during operation", eName, id());
            return this;
        }
        if (stopped.get()) {
            ep().log.error("ENET({},{}): Closed and cannot be changed", eName, id());
            return this;
        }
        track = true; return this;
    }


    /**
     * 是否是debug模式
     */
    public boolean isTrack() { return track; }


    public EC args(Object... args) {
        if (stopped.get()) {
            ep().log.error("ENET({},{}): Closed and cannot be changed", eName, id());
            return this;
        }
        this.args = args; return this;
    }


    /**
     * 设置id
     * @param id 事件id
     */
    public EC id(String id) {
        if (ep != null) {
            ep().log.error("ENET({},{}): Cannot be changed during operation", eName, id());
            return this;
        }
        if (stopped.get()) {
            ep().log.error("ENET({},{}): Closed and cannot be changed", eName, id());
            return this;
        }
        this.id = id; return this;
    }


    /**
     * 当前事件id
     */
    public String id() {return this.id;}

    public Object source() { return source; }

    public EC source(Object s) {
        if (ep != null) {
            ep().log.error("ENET({},{}): Cannot be changed during operation", eName, id());
            return this;
        }
        if (stopped.get()) {
            ep().log.error("ENET({},{}): Closed and cannot be changed", eName, id());
            return this;
        }
        this.source = s;
        return this;
    }


    /**
     * 返回第一个异常
     * @return 没结束返回null
     */
    public Throwable ex() {
        return isStopped() ? results.stream().filter(r -> r.ex != null).map(r -> r.ex).findFirst().orElse(null) : null;
    }

    /**
     * 所有监听器执行的异常
     * @return 没结束返回null
     */
    public List<Throwable> exs() { return isStopped() ? results.stream().map(r -> r.ex).collect(Collectors.toList()) : null; }

    /**
     * 取第一个结果
     * @return 没结束返回null
     */
    public Object result() {
        // fix: results 同时在加, 这里又在遍历, 导致的ConcurrentModificationException
        return isStopped() ? results.stream().filter(r -> r.value != null).map(r -> r.value).findFirst().orElse(null) : null;
    }


    /**
     * 所有结果
     * @return 没结束返回null
     */
    public List<Object> results() { return isStopped() ? results.stream().map(r -> r.value).collect(Collectors.toList()) : null; }

    public List<ResultRecord> originResults() { return results; }

    protected EC addResult(Listener listener, Throwable ex, Object r) {
        if (stopped.get()) {
            ep().log.error("ENET({},{}): Closed and cannot be changed", eName, id());
            return this;
        }
        synchronized (this) {
            results.add(new ResultRecord(listener, ex, r));
        }
        return this;
    }


    public EP ep() { return ep; }


    public boolean isStopped() { return stopped.get(); }


    public EC attr(Object key, Object value) {
        if (stopped.get()) {
            ep().log.error("ENET({},{}): Closed and cannot be changed", eName, id());
            return this;
        }
        attrs.put(key, value);
        return this;
    }


    public <T> T getAttr(Object key, Class<T> type, T defaultValue) {
        if (attrs == null) return defaultValue;
        return type.cast(attrs.getOrDefault(key, defaultValue));
    }


    public <T> T getAttr(Object key, Class<T> type) { return getAttr(key, type, null); }


    public Object getAttr(Object key) { return attrs == null ? null : attrs.get(key); }


    /**
     * 每个监听器对应的执行结果
     */
    public class ResultRecord {
        public Listener listener;
        public Throwable ex;
        public Object value;

        public ResultRecord(Listener listener, Throwable ex, Object value) {
            this.listener = listener;
            this.ex = ex;
            this.value = value;
        }
    }
}
